#pragma once
#include<iostream>
#include<string>
#include<queue>
using namespace std;

const static int defaultcap = 5;
template<typename T>
class BlockQueue
{
    private:
    bool IsFull()//判断队列是否满了
    {
        return _block_queue.size()==_max_cap;
    }
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
    public:
        BlockQueue(int cap = defaultcap):_max_cap(cap)
        {
            pthread_mutex_init(&_mutex,nullptr);
            pthread_cond_init(&_p_cond,nullptr);
            pthread_cond_init(&_c_cond,nullptr);
        }

        //假设2个消费者
        void Pop(T* out)//出队列    清楚队列是否为满
        {
            pthread_mutex_lock(&_mutex);
            while(IsEmpty())
            {
                //添加尚未满足，但是线程被异常唤醒的情况叫做伪唤醒
                //队列为空，必须等待
                pthread_cond_wait(&_c_cond,&_mutex);//假设两个消费者在等待
            }
            //不为空/被唤醒了
            *out = _block_queue.front();
            _block_queue.pop();

            pthread_mutex_unlock(&_mutex);

            pthread_cond_signal(&_p_cond);//唤醒生产者
        }

        //假设1个生产者
        void Equeue(const T& in)//入队列   清楚队列是否为空
        {
            pthread_mutex_lock(&_mutex);
            while(IsFull())
            {
            // 满了，生产者不能生产，必须等待
            // 可是在临界区里面啊！！！pthread_cond_wait函数的用法如下：
            // 被调用的时候：除了让自己继续排队等待，还会自己释放传入的锁
            // 函数返回的时候，不就还在临界区了！
            // 返回时：必须先参与锁的竞争，重新加上锁，该函数才会返回！
                pthread_cond_wait(&_p_cond,&_mutex);
            }
            //没有满/被唤醒了
            _block_queue.push(in);//生产到阻塞队列

            pthread_mutex_unlock(&_mutex);

            //让消费者消费
            pthread_cond_signal(&_c_cond);//唤醒消费者;放在unlock前后都可以
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_p_cond);
            pthread_cond_destroy(&_c_cond);
        }
    private:
        queue<T> _block_queue;//临界资源（共享资源）
        int _max_cap;//最大容量
        pthread_mutex_t _mutex;//加锁（互斥）
        pthread_cond_t _p_cond;//生产者条件变量
        pthread_cond_t _c_cond;//消费者条件变量

};